﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Printing;
using System.Data;

namespace nr.Reports
{
    public class ReportCompiler
    {
        /// <summary>
        /// numero di pagina corrente
        /// </summary>
        private int pageNumber;
        /// <summary>
        /// larghezze delle colonne in stampa
        /// </summary>
        private RectangleF[] columnsWidth;
        /// <summary>
        /// ultimo record stampato
        /// </summary>
        private int currentRecord;
        /// <summary>
        /// nome del report
        /// </summary>
        private string name;
        /// <summary>
        /// stile dle report
        /// </summary>
        private ReportStyle style;
        /// <summary>
        /// documento su cui stampare il report
        /// </summary>
        private PrintDocument document;
        /// <summary>
        /// sorgente dati
        /// </summary>
        private DataView data;
        /// <summary>
        /// Legge/Imposta l'indice dell'ultimo record stampato
        /// </summary>
        private int CurrentRecord { get { return currentRecord; } set { currentRecord = value; } }
        /// <summary>
        /// Legge/Imposta il numero di pagina in stampa
        /// </summary>
        private int PageNumber { get { return pageNumber; } set { pageNumber = value; } }
        /// <summary>
        /// Legge/Imposta il nome del report
        /// </summary>
        public string Name { get { return name; } set { name = value; } }
        /// <summary>
        /// Legge/Imposta lo stile del report
        /// </summary>
        public ReportStyle Style { get { return style; } set { style = value; } }
        /// <summary>
        /// Legge/Imposta il documento su cui stampare il report
        /// </summary>
        public PrintDocument Document
        {
            get { return document; }
            set
            {
                if (document != value)
                {
                    document = value;
                    document.BeginPrint += new PrintEventHandler(document_BeginPrint);
                    document.EndPrint += new PrintEventHandler(document_EndPrint);
                    document.DocumentName = string.Format("[nrReport] {0}", Name);
                    document.PrintPage += new PrintPageEventHandler(document_PrintPage);
                }
            }
        }
        /// <summary>
        /// Sposta CurrentRecord sul prossimo record da stampare
        /// </summary>
        /// <returns>False se non ci sono piu' record da stampare</returns>
        protected void NextRecord()
        {
            if (CurrentRecord < (Data.Table.Rows.Count)) ++CurrentRecord;
        }
        /// <summary>
        /// Controlla se ci sono ancora record da stampare
        /// </summary>
        /// <returns>False se non ci sono piu' record da stampare</returns>
        protected bool HaveMoreRecords()
        {
            return CurrentRecord < Data.Table.Rows.Count;
        }
        /// <summary>
        /// Stampa una pagina del report
        /// </summary>
        /// <param name="sender">Oggetto che ha chiesto la stampa</param>
        /// <param name="e">Parametri di stampa</param>
        void document_PrintPage(object sender, PrintPageEventArgs e)
        {
            // incrementa il numero di pagina corrente
            ++PageNumber;
            InitializePrintParameters(e);
            // contatore di record nella pagina
            int recordCount = 0;
            // spazio disponibile sulla pagina
            SizeF availableSize = new SizeF(e.PageSettings.Bounds.Width - e.PageSettings.Margins.Left - e.PageSettings.Margins.Right, e.PageSettings.Bounds.Height - e.PageSettings.Margins.Top - e.PageSettings.Margins.Bottom);
            // posizione corrente della prossima riga
            PointF currentLocation = new PointF(e.PageSettings.Margins.Left, e.PageSettings.Margins.Top);
            if (Style.HeaderVisible) currentLocation.Y += PrintHeader(e);
            // dimensioni del testo stampato
            SizeF textSize = SizeF.Empty;
            // controlla se c'e' spazio in pagina
            bool pageOverflow = false;
            // stampa fino a fine paginao fino a fine tabella 
            while (HaveMoreRecords() && !pageOverflow)
            {
                // reimposta il margine sinistro della riga
                currentLocation.X = e.PageSettings.Margins.Left;
                // stampa le colonne della riga corrente
                for (int index = 0; index < Style.ColumnStyles; ++index)
                {
                    // imposta lo stile corrente sulla base dell'indice
                    ReportRowStyle cs = (index % 2 == 0) ? Style.AlternatingItem : Style.Item;
                    // stile della colonna corrente
                    ReportColumnStyle columnStyle = Style.GetColumnStyle(index);
                    // testo formattato
                    string text = string.Format(columnStyle.TextFormat, Data[CurrentRecord][columnStyle.FieldName]);
                    // calcola le dimensioni del testo
                    textSize = e.Graphics.MeasureString(text, cs.Font);
                    // disegna lo sfondo della cella
                    e.Graphics.FillRectangle(cs.BackColor, currentLocation.X, currentLocation.Y, columnsWidth[index].Width, textSize.Height);
                    // disegna il bordo della cella
                    e.Graphics.DrawRectangle(cs.Border, currentLocation.X, currentLocation.Y, columnsWidth[index].Width, textSize.Height);
                    // imposta il formato del testo
                    StringFormat sf = new StringFormat();
                    sf.Alignment = columnStyle.Alignment;
                    sf.Trimming = StringTrimming.EllipsisCharacter;
                    // stampa il testo nella colonna
                    e.Graphics.DrawString(text, cs.Font, Style.ForeColor, new RectangleF(currentLocation.X, currentLocation.Y, columnsWidth[index].Width, textSize.Height), sf);
                    // incrementa la posizione orizzontale
                    currentLocation.X += columnsWidth[index].Width;
                }
                // si sposta al prossimo record
                NextRecord();
                // incrementa il contatore di riga
                ++recordCount;
                // incrementa la posizione verticale
                currentLocation.Y += textSize.Height;
                // controlla se c'e' spazio in pagina
                pageOverflow = (currentLocation.Y + textSize.Height) > availableSize.Height;
            }
            if (Style.FooterVisible) PrintFooter(e);
            // controlla se ci sono ancora record
            e.HasMorePages = HaveMoreRecords();
        }
        /// <summary>
        /// Stampa il pie' di pagina del report
        /// </summary>
        /// <param name="pageSettings">Impostazioni di pagina</param>
        private void PrintFooter(PrintPageEventArgs e)
        {
        }
        /// <summary>
        /// Stampa l'intestazione del report
        /// </summary>
        /// <param name="pageSettings">Impostazioni di pagina</param>
        private float PrintHeader(PrintPageEventArgs e)
        {
            float height = 0;
            float left = 0;
            for (int index = 0; index < Style.ColumnStyles; ++index)
            {
                // stile della colonna corrente
                ReportColumnStyle columnStyle = Style.GetColumnStyle(index);
                SizeF size = e.Graphics.MeasureString(columnStyle.HeaderText, Style.Header.Font);
                if (size.Height > height) height = size.Height;
                RectangleF rect = new RectangleF(new PointF(e.PageSettings.Margins.Left + left, e.PageSettings.Margins.Top), new SizeF(columnsWidth[index].Width, size.Height));
                left += columnsWidth[index].Width;
                e.Graphics.FillRectangle(Style.Header.BackColor, rect);
                e.Graphics.DrawRectangle(Style.Header.Border, rect.Left, rect.Top, rect.Width, rect.Height);
                StringFormat sf = new StringFormat();
                sf.Alignment = Style.Header.Alignment;
                sf.Trimming = StringTrimming.Character;
                e.Graphics.DrawString(columnStyle.HeaderText, Style.Header.Font, Style.ForeColor, rect, sf);
            }

            return height;
        }
        /// <summary>
        /// Legge/Imposta la sorgente dati del report
        /// </summary>
        public DataView Data { get { return data; } set { data = value; } }
        /// <summary>
        /// Termina la stampa del report
        /// </summary>
        /// <param name="sender">Oggetto che ha chiesto la terminazione della stampa</param>
        /// <param name="e">Parametri di stampa</param>
        private void document_EndPrint(object sender, PrintEventArgs e) { }
        /// <summary>
        /// Inizia la stampa del report
        /// </summary>
        /// <param name="sender">Oggetto che ha chiesto l'inizio della stampa</param>
        /// <param name="e">Parametri di stampa</param>
        private void document_BeginPrint(object sender, PrintEventArgs e)
        {
            if (Data == null)
                e.Cancel = true;
            CurrentRecord = 0;
            PageNumber = 0;
        }
        /// <summary>
        /// Inizializza i parametri di stampa
        /// </summary>
        protected virtual void InitializePrintParameters(PrintPageEventArgs e)
        {
            columnsWidth = new RectangleF[Style.ColumnStyles];
            float left = e.PageSettings.Margins.Left;
            float top = e.PageSettings.Margins.Top;
            float height = 0;
            float sum = 0;
            for (int index = 0; index < Style.ColumnStyles; ++index)
            {
                ReportColumnStyle col = Style.GetColumnStyle(index);
                columnsWidth[index] = new RectangleF(left, top, (int)(col.Width == -1 ? 100 : col.Width), height);
                sum += columnsWidth[index].Width;
            }
            float availableWidth = e.PageBounds.Width - e.PageSettings.Margins.Left - e.PageSettings.Margins.Right;
            for (int index = 0; index < Style.ColumnStyles; ++index)
            {
                // c : sum = x : w
                // x = c * w / sum
                columnsWidth[index].Width = columnsWidth[index].Width * availableWidth / sum;
            }
        }
        /// <summary>
        /// Costruttore di default
        /// </summary>
        public ReportCompiler()
        {
            Style = new ReportStyle();
            Document = null;
            Data = null;
            Name = Properties.Resources.strNoNamedReport;
        }
    }
}
